package com.texaco.chapasFutbol.multiplayer;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import org.andengine.util.call.Callback;
import org.andengine.util.debug.Debug;
import org.json.JSONException;
import org.json.JSONObject;

import com.texaco.chapasFutbol.multiplayer.mensajes.AckLoginMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.AckMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.BaseMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.EndMatchMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.GoalMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.IMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.LeaveQueueMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.LoginMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.MatchMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.PositionsMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.QuitMatchMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.RequestUpdateMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.ResumeMatchMessage;
import com.texaco.chapasFutbol.multiplayer.mensajes.ShootMessage;


/**
 * Helper class used to communicate with the demo server.
 */
public final class ServerUtilities {

    private static final int MAX_ATTEMPTS = 5;
    private static final int BACKOFF_MILLI_SECONDS = 2000;
    private static final Random random = new Random();

	private static final String ENDPOINT = "http://mtexttest2.appspot.com/testapp/test";

	/**
     * Issue a POST request to the server.
     *
     * @param endpoint POST address.
     * @param params request parameters.
     *
     * @return String response body
     * @throws IOException propagated from POST.
     */
    private static String post(String endpoint, Map<String, String> params)
            throws IOException {
    	System.out.println("POST - Solicitud: " + params);
        URL url;
    	StringBuffer respBody = null;
        try {
            url = new URL(endpoint);
        } catch (MalformedURLException e) {
            throw new IllegalArgumentException("invalid url: " + endpoint);
        }
        StringBuilder bodyBuilder = new StringBuilder();
        if(params != null){
        	Iterator<Entry<String, String>> iterator = params.entrySet().iterator();
        	// constructs the POST body using the parameters
        	while (iterator.hasNext()) {
        		Entry<String, String> param = iterator.next();
        		bodyBuilder.append(param.getKey()).append('=')
        		.append(param.getValue());
        		if (iterator.hasNext()) {
        			bodyBuilder.append('&');
        		}
        	}
        }
        String body = bodyBuilder.toString();
        byte[] bytes = body.getBytes();
        HttpURLConnection conn = null;
        int attempt = 0;
        boolean success = false;
        String status = new String();
        while(!success && attempt++ < MAX_ATTEMPTS){
        	try {
        		conn = (HttpURLConnection) url.openConnection();
        		conn.setDoOutput(true);
        		conn.setUseCaches(false);
        		conn.setFixedLengthStreamingMode(bytes.length);
        		conn.setRequestMethod("POST");
        		conn.setRequestProperty("Content-Type",
        				"application/x-www-form-urlencoded;charset=UTF-8");
        		// post the request
        		OutputStream out = conn.getOutputStream();
        		out.write(bytes);
        		out.close();
        		// handle the response
        		if (conn.getResponseCode() != 200) {
        			status += ", " + conn.getResponseCode();
//        			throw new IOException("Post failed with error code " + status);
        		}else{
        			BufferedReader in = new BufferedReader(new InputStreamReader(
        					conn.getInputStream()));
        			respBody = new StringBuffer();
        			String inputLine;
        			while ((inputLine = in.readLine()) != null)
        				respBody.append(inputLine);
        			
        			System.out.println("POST - Respuesta: " + respBody);
        			if(respBody.length() > 0){
        				success = true;
        			}
        		}
        	}catch (Throwable th){
        		System.out.println("**** Error en multiplayer capturado *****" + th);
        		th.printStackTrace();
        	} finally {
        		if (conn != null) {
        			conn.disconnect();
        		}
        	}
        	
        }
        
        if(!success){
        	System.err.println("Post failed with error code " + status);
			throw new IOException("Post failed with error code " + status);
        }
        return respBody.toString();
      }

	public static BaseMessage callLogin() throws JSONException, IOException {
		LoginMessage message = new LoginMessage();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callAckLogin() throws IOException, JSONException {
		AckLoginMessage message = new AckLoginMessage();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callGoal(final int order, final long idMatch, final long idClient, final String team, final Callback<Exception> onErrorCallback) {
		new Thread(new Runnable() {
			public void run() {
				GoalMessage message;
				try {
					message = new GoalMessage();
					message.setOrder(order);
					message.setIdMatch(idMatch);
					message.setIdClient(idClient);
					message.setTeam(team);
					post(ENDPOINT, message.getMap());
				} catch (JSONException e) {
					Debug.d("Error de json: " + e.getCause() + " " + e.getMessage());
				} catch (IOException e) {
					onErrorCallback.onCallback(e);
				}
			}
		}).start();
		return null;
	}

	public static BaseMessage callResumeMatch(int order, long idClient, long idMatch) throws JSONException, IOException {
		ResumeMatchMessage message = new ResumeMatchMessage();
		message.setOrder(order);
		message.setIdClient(idClient);
		message.setIdMatch(idMatch);
		message.write();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callQuitMatch(final int order, final long idClient, final long idMatch){
		new Thread(new Runnable() {
			public void run() {
				QuitMatchMessage message;
				try {
					message = new QuitMatchMessage();
					message.setOrder(order);
					message.setIdClient(idClient);
					message.setIdMatch(idMatch);
					post(ENDPOINT, message.getMap());
				} catch (JSONException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}).start();
		return null;
	}

	public static BaseMessage callEndMatch(final int order, final long idMatch){
		new Thread(new Runnable() {
			public void run() {
				EndMatchMessage message;
				try {
					message = new EndMatchMessage();
					message.setOrder(order);
					message.setIdMatch(idMatch);
					message.write();
					post(ENDPOINT, message.getMap());
				} catch (JSONException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}).start();
		return null;
	}

	public static BaseMessage callShoot(final int order, final long idClient, final long idMatch, 
			final int idChapa, final float x, final float y, final Callback<Exception> onErrorCallback) throws IOException, JSONException {
		new Thread(new Runnable() {
			public void run() {
				ShootMessage message;
				try {
					message = new ShootMessage();
					message.setOrder(order);
					message.setIdClient(idClient);
					message.setIdMatch(idMatch);
					message.setIdChapa(idChapa);
					message.setX(x);
					message.setY(y);
					message.write();
					post(ENDPOINT, message.getMap());
				} catch (JSONException e) {
					Debug.d("Error de json: " + e.getCause() + " " + e.getMessage());
				} catch (IOException e) {
					onErrorCallback.onCallback(e);
				}
			}
		}).start();
		return null;
	}

	public static BaseMessage callAck() throws IOException, JSONException {
		AckMessage message = new AckMessage();
		return processResponse(post(ENDPOINT, message.getMap()));	
	}

	public static BaseMessage callLeaveQueue(long idClient) throws IOException, JSONException {
		LeaveQueueMessage message = new LeaveQueueMessage();
		message.setIdClient(idClient);
		message.write();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callRequestUpdate(long idClient, int order) throws IOException, JSONException {
		RequestUpdateMessage message = new RequestUpdateMessage();
		message.setIdClient(idClient);
		message.setOrder(order);
		message.write();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callMatch() throws IOException, JSONException {
		MatchMessage message = new MatchMessage();
		return processResponse(post(ENDPOINT, message.getMap()));
	}

	public static BaseMessage callUpdatePositions(int order, long idClient, long idMatch, 
			List<Float> capsLPos, final float ballX, final float ballY, final Callback<Exception> onErrorCallback) throws IOException, JSONException {
		final PositionsMessage message = new PositionsMessage();
		message.setIdClient(idClient);
		message.setOrder(order);
		message.setIdMatch(idMatch);
		List<Double> pos = new ArrayList<Double>();
		for(float p : capsLPos){
			pos.add((double) p);
		}
		message.setCapsPos(pos);
		message.setBallPos(new ArrayList<Double>(){{add((double) ballX);add((double) ballY);}});
		new Thread(new Runnable() {
			public void run() {
				try {
					processResponse(post(ENDPOINT, message.getMap()));
				} catch (JSONException e) {
					Debug.d("Error de json: " + e.getCause() + " " + e.getMessage());
				} catch (IOException e) {
					onErrorCallback.onCallback(e);
				}
			}
		}).start();
		return null;
	}

	private static BaseMessage processResponse(String resp) throws JSONException {
		System.out.println("processResponse: mensaje recibido: " + resp);
		BaseMessage message = null;
		JSONObject json = new JSONObject(resp);
		int idMessage = json.getInt(IMessage.ID_MESSAGE);
		if(idMessage == IMessage.MESSAGE_ACK){
			message = new AckMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_ACK_LOGIN){
			message = new AckLoginMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_END_MATCH){
			message = new EndMatchMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_GOAL){
			message = new GoalMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_MACH){
			message = new MatchMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_QUIT_MATCH){
			message = new QuitMatchMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_RESUME_MATCH){
			message = new ResumeMatchMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_SHOOT){
			message = new ShootMessage(resp);
		}else if(idMessage == IMessage.MESSAGE_POSITIONS){
			message = new PositionsMessage(resp);
		}
		System.out.println("processResponse: mensaje devuelto: " + message.toString());
		return message;
	}
}